#include "Renderer.hpp"

static float lightModelView[16];
static float lightProjection[16];

const float bias[] = {0.5, 0.0, 0.0, 0.0, 
					  0.0, 0.5, 0.0, 0.0,
					  0.0, 0.0, 0.5, 0.0,
					  0.5, 0.5, 0.5, 1.0 };




float lightPos[4] = { 0, 300,  300, 1 };


OffscreenSurface Renderer::shadowBuffer;

Camera* Renderer::m_camera;
QList<Page*> Renderer::m_pages;

int Renderer::m_windowWidth;
int Renderer::m_windowHeight;

Shader* Renderer::m_groundShader;

float Renderer::aspect;

Texture* Renderer::m_grass = NULL;


void Renderer::initialize()
{
    Logger::write( "Renderer", "initialize() @ 1", "Begin", Logger::Note );
	glMatrixMode( GL_MODELVIEW_MATRIX );
	glPushMatrix();
	
	glLoadIdentity();
	gluPerspective( 45.0, 1, 2.00f, 800.0f );
	glGetFloatv(GL_MODELVIEW_MATRIX, lightProjection);		
	
	glLoadIdentity();
	gluLookAt( lightPos[0], lightPos[1], lightPos[2],
 			   lightPos[0], lightPos[1]-1, lightPos[2]-1,
			   0, 1, 0 );
	glGetFloatv(GL_MODELVIEW_MATRIX, lightModelView);

	
	glPopMatrix();

	
	
	
	GLenum errCode = glGetError();
	if (errCode != GL_NO_ERROR)
	{
        string errString = (const char*)gluErrorString(errCode);
        Logger::write( "Renderer", "initialize() @ 2", errString.c_str(), Logger::Error );
	}
	

	
	glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);	
	glClearColor(0,0.5,0.5,1);
	

	shadowBuffer.createDepth();	
    ShaderLibrary::add( new Shader( ":/shaders/litVert.glsl", ":/shaders/litFrag.glsl" ), "defaultShader" );
    m_grass = getTexture( ":/textures/grass.png" );
	m_groundShader = ShaderLibrary::get( "defaultShader" );	
	
	errCode = glGetError();
	if (errCode != GL_NO_ERROR)
	{
        string errString = (const char*)gluErrorString(errCode);
        Logger::write( "Renderer", "initialize() @ 3", errString.c_str(), Logger::Error );
	}
	
	errCode = glGetError();
	if (errCode != GL_NO_ERROR)
	{
        string errString = (const char*)gluErrorString(errCode);
        Logger::write( "Renderer", "initialize() @ 4", errString.c_str(), Logger::Error );
	}

	Page* page = new Page();
	
	errCode = glGetError();
	if (errCode != GL_NO_ERROR)
	{
        string errString = (const char*)gluErrorString(errCode);
        Logger::write( "Renderer", "initialize() @ 5", errString.c_str(), Logger::Error );
	}
	
	page->generate();
	
	errCode = glGetError();
	if (errCode != GL_NO_ERROR)
	{
        string errString = (const char*)gluErrorString(errCode);
        Logger::write( "Renderer", "initialize() @ 6", errString.c_str(), Logger::Error );
	}
	
	m_pages.push_front( page );
    Logger::write( "Renderer", "initialize() @ 7", "End", Logger::Note );
}




void Renderer::resize( int width, int height )
{
	m_windowWidth = width;
	m_windowHeight = height;
	aspect = float(m_windowWidth)/float(m_windowHeight );
	glViewport(0,0, m_windowWidth, m_windowHeight );
	assert( glGetError() == GL_NO_ERROR );
}


void Renderer::shadowPass()
{
	glCullFace( GL_FRONT );
	shadowBuffer.bind();
	glViewport(0,0, shadowBuffer.getWidth(), shadowBuffer.getHeight() );
    glUseProgram( (GLuint)NULL );
	
	glClear( GL_DEPTH_BUFFER_BIT );
	glColorMask(false, false, false, false );
	glPolygonOffset(2.0f, 4.0f);
	glEnable(GL_POLYGON_OFFSET_FILL);
	

	glPushMatrix();
	setupShadowMatrix();
	
	foreach( Page *cur, m_pages ) {
		cur->drawShadow();
	}

	
	glPopMatrix(); // Reset modelview
	
	shadowBuffer.unbind();
}

void Renderer::setupShadowMatrix()
{

	
	
	// set up matricies
	glMatrixMode(GL_PROJECTION);
	glLoadMatrixf( lightProjection );
		
	// Save modelview and update to light position
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt( lightPos[0], lightPos[1], lightPos[2],
			  lightPos[0], lightPos[1]-1, lightPos[2]-1,
			  0, 1, 0 );
	glGetFloatv(GL_MODELVIEW_MATRIX, lightModelView);

	glLoadMatrixf( lightModelView );

	
	
	glActiveTexture( GL_TEXTURE2 );
	glEnable( GL_TEXTURE_2D );
	glBindTexture( GL_TEXTURE_2D, shadowBuffer.getDepth() );
	
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);

	glMatrixMode( GL_TEXTURE );
	glLoadMatrixf( bias );
	glMultMatrixf( lightProjection );
	glMultMatrixf( lightModelView );
	
//	Matrix4 m = m_camera->getInverseMatrix();
//	glMultMatrixf( (float*)m );
	
	glActiveTexture( GL_TEXTURE0 );
	glMatrixMode( GL_MODELVIEW );
}

void Renderer::renderScene()
{
	glCullFace( GL_BACK );
	
	glViewport(0,0, m_windowWidth, m_windowHeight );
	glColorMask(true, true, true, true );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective( 45.0, aspect, 1.00f, 800.0f );
//	glMultMatrixf( lightProjection );
		
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	
//	// set light position
	glLightfv(GL_LIGHT0,GL_POSITION,lightPos);
	
	
	//glMultMatrixf( lightModelView );
	m_camera->apply();
	assert( glGetError() == GL_NO_ERROR );

	
	foreach( Page *cur, m_pages )
	{
		cur->drawSimple();
	}

	
	if( m_groundShader )
		m_groundShader->bind();
	
	if( m_grass )
		m_grass->bind();
	
	glBegin( GL_QUADS );
	
    glNormal3f  (    0.0,   1.0,    0.0 );
    glColor3f   (    1.0,   1.0,    1.0 );
    glTexCoord2f(    0.0,   0.0         );
    glVertex3f  ( -400.0,   0.0, -400.0 );
	
    glTexCoord2f(    0.0, 400.0         );
    glVertex3f  ( -400.0,   0.0,  400.0 );
				 
    glTexCoord2f(  400.0, 400.0         );
    glVertex3f  (  400.0,   0.0,  400.0 );
							  
    glTexCoord2f(  400.0,   0.0         );
    glVertex3f  (  400.0,   0.0, -400.0 );
	glEnd();
	
	if( m_grass )
		m_grass->unbind();

	if( m_groundShader )
		m_groundShader->unbind();
	
//	glUseProgramObjectARB(0);
//	glMatrixMode(GL_PROJECTION);
//	glLoadIdentity();
//	glOrtho(-shadowBuffer.getWidth()/2,shadowBuffer.getWidth()/2,-shadowBuffer.getHeight()/2,shadowBuffer.getHeight()/2,1,20);
//	glMatrixMode(GL_MODELVIEW);
//	glLoadIdentity();
//	glColor4f(1,1,1,1);
//	glActiveTextureARB(GL_TEXTURE0);
//	glBindTexture(GL_TEXTURE_2D, shadowBuffer.getDepth() );
//	glEnable(GL_TEXTURE_2D);
//	glTranslated(0,0,-1);
//	glBegin(GL_QUADS);
//	glTexCoord2d(0,0);glVertex3f(0,0,0);
//	glTexCoord2d(1,0);glVertex3f(shadowBuffer.getWidth()/2,0,0);
//	glTexCoord2d(1,1);glVertex3f(shadowBuffer.getWidth()/2,shadowBuffer.getHeight()/2,0);
//	glTexCoord2d(0,1);glVertex3f(0,shadowBuffer.getHeight()/2,0);
//	
//	
//	glEnd();
//	glDisable(GL_TEXTURE_2D);
	
	
	glPopMatrix();
}


void Renderer::frame()
{
	shadowPass();
	renderScene();
}

